Skip to content

[feat] support Kubernetes Gateway API#6347

Open
eye-gu wants to merge 3 commits into
apache:masterfrom
eye-gu:fix-6346
Open

[feat] support Kubernetes Gateway API#6347
eye-gu wants to merge 3 commits into
apache:masterfrom
eye-gu:fix-6346

Conversation

@eye-gu
Copy link
Copy Markdown
Contributor

@eye-gu eye-gu commented May 18, 2026

close #6346

Implements ShenYu support for Kubernetes Gateway API (gateway.networking.k8s.io/v1), complementing the existing Ingress support.

Core Components

Component Description
GatewayClassReconciler Watches GatewayClass, accepts those with spec.controllerName=shenyu
GatewayReconciler Watches Gateway, re-queues affected HTTPRoutes on Gateway changes
HTTPRouteReconciler Watches HTTPRoute, parses into ShenYu selector/rule via HttpRouteParser
HttpRouteParser Translates HTTPRoute spec (hostnames + matches + backendRefs) into SelectorData/RuleData
GatewayRouteCache Thread-safe cache for route↔selector and gateway↔route bindings
GatewayApiControllerConfiguration Spring Boot auto-configuration for all Gateway API beans

Make sure that:

  • You have read the contribution guidelines.
  • You submit test cases (unit or integration tests) that back your changes.
  • Your local test passed ./mvnw clean install -Dmaven.javadoc.skip=true.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Kubernetes Gateway API (gateway.networking.k8s.io/v1) support to ShenYu’s k8s controller/starter, alongside existing Ingress support, and introduces a new integrated test workflow to validate Gateway API routing.

Changes:

  • Adds Spring Boot auto-configuration and controllers/reconcilers for GatewayClass, Gateway, and HTTPRoute.
  • Implements HttpRouteParser + GatewayRouteCache to translate HTTPRoute specs into ShenYu selector/rule config and track bindings.
  • Introduces a new k8s Gateway API integrated test module and GitHub Actions workflow to run it on kind.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
shenyu-spring-boot-starter/shenyu-spring-boot-starter-k8s/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports Registers the new Gateway API auto-configuration.
shenyu-spring-boot-starter/shenyu-spring-boot-starter-k8s/src/main/resources/META-INF/spring.factories Registers Gateway API auto-configuration for legacy Spring Boot loading.
shenyu-spring-boot-starter/shenyu-spring-boot-starter-k8s/src/main/java/org/apache/shenyu/springboot/starter/k8s/IngressControllerConfiguration.java Adds shenyu.k8s.mode gating and fixes default secret TLS loading condition.
shenyu-spring-boot-starter/shenyu-spring-boot-starter-k8s/src/main/java/org/apache/shenyu/springboot/starter/k8s/GatewayApiControllerConfiguration.java New Gateway API controller wiring (informers/controllers/reconcilers/repository bootstrap).
shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/reconciler/GatewayClassReconciler.java New GatewayClass reconciliation + status patch + requeue logic.
shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/reconciler/GatewayReconciler.java New Gateway reconciliation, status patching, and HTTPRoute requeueing.
shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/reconciler/HTTPRouteReconciler.java New HTTPRoute reconciliation, config apply/delete, binding, and status patching.
shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/parser/HttpRouteParser.java New HTTPRoute→selector/rule translation logic.
shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/common/GatewayApiConstants.java Gateway API constants + shared condition helper.
shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/cache/GatewayRouteCache.java New thread-safe cache for route↔selector and gateway↔route bindings.
shenyu-kubernetes-controller/src/test/java/org/apache/shenyu/k8s/GatewayReconcilerTest.java Unit tests for Gateway reconciliation behaviors.
shenyu-kubernetes-controller/src/test/java/org/apache/shenyu/k8s/HTTPRouteReconcilerTest.java Unit tests for HTTPRoute reconciliation behaviors.
shenyu-kubernetes-controller/src/test/java/org/apache/shenyu/k8s/HttpRouteParserTest.java Unit tests for HTTPRoute parsing/mapping logic.
shenyu-integrated-test/pom.xml Adds the new Gateway API integrated test module to the build.
shenyu-integrated-test/shenyu-integrated-test-k8s-gateway-api-http/** New integrated test module (app, config, Dockerfile, kind manifests, scripts, tests).
shenyu-examples/shenyu-examples-http/k8s/gateway-api.yml Example GatewayClass/Gateway/HTTPRoute manifests for the HTTP example.
.github/workflows/integrated-test-k8s-gateway-api.yml New CI workflow to run Gateway API integrated tests on kind.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +121 to +127
@Bean("gatewayclass-controller-manager")
public ControllerManager gatewayClassControllerManager(
@Qualifier("gatewayclass-shared-informer-factory") final SharedInformerFactory gatewayClassFactory,
@Qualifier("gatewayclass-controller") final Controller gatewayClassController) {
ControllerManager controllerManager = new ControllerManager(gatewayClassFactory, gatewayClassController);
Executors.newSingleThreadExecutor().submit(controllerManager);
return controllerManager;
Comment on lines +236 to +242
@Bean
public ShenyuCacheRepository shenyuCacheRepository(final CommonPluginDataSubscriber pluginDataSubscriber,
final CommonDiscoveryUpstreamDataSubscriber discoveryUpstreamDataSubscriber,
final MetaDataCacheSubscriber metaDataSubscriber,
final MetaDataCacheSubscriber metaDataCacheSubscriber) {
ShenyuCacheRepository repository = new ShenyuCacheRepository(pluginDataSubscriber, discoveryUpstreamDataSubscriber, metaDataSubscriber, metaDataCacheSubscriber);
enablePlugin(repository, PluginEnum.GLOBAL, null);
Comment on lines +238 to +266
/**
* Check if the HTTPRoute already has Accepted=True condition from the ShenYu controller
* in its status.parents, to avoid unnecessary status patches that trigger infinite reconcile loops.
*/
private boolean isRouteStatusAlreadySet(final DynamicKubernetesObject httpRoute) {
JsonObject raw = httpRoute.getRaw();
if (!raw.has("status") || raw.get("status").isJsonNull()) {
return false;
}
JsonObject status = raw.getAsJsonObject("status");
if (!status.has("parents") || status.get("parents").isJsonNull()) {
return false;
}
JsonArray parents = status.getAsJsonArray("parents");
for (JsonElement parentElement : parents) {
JsonObject parent = parentElement.getAsJsonObject();
if (!parent.has("controllerName") || !GatewayApiConstants.SHENYU_CONTROLLER_NAME.equals(parent.get("controllerName").getAsString())) {
continue;
}
if (!parent.has("conditions") || parent.get("conditions").isJsonNull()) {
continue;
}
JsonArray conditions = parent.getAsJsonArray("conditions");
for (JsonElement condElement : conditions) {
JsonObject cond = condElement.getAsJsonObject();
if ("Accepted".equals(cond.has("type") ? cond.get("type").getAsString() : null)
&& "True".equals(cond.has("status") ? cond.get("status").getAsString() : null)) {
return true;
}
Comment on lines +108 to +134
private void requeueAffectedHTTPRoutes(final String gatewayNamespace, final String gatewayName) {
// Search in the gateway's namespace (same-namespace reference)
List<DynamicKubernetesObject> localRoutes = httpRouteLister.namespace(gatewayNamespace).list();
for (DynamicKubernetesObject route : localRoutes) {
if (isBoundToGateway(route, gatewayNamespace, gatewayName)) {
Request req = new Request(route.getMetadata().getNamespace(), route.getMetadata().getName());
httpRouteWorkQueue.add(req);
LOG.info("Re-queued HTTPRoute {}/{} due to Gateway {}/{} reconciliation",
route.getMetadata().getNamespace(), route.getMetadata().getName(),
gatewayNamespace, gatewayName);
}
}
// Also search all namespaces for cross-namespace references
for (DynamicKubernetesObject route : httpRouteLister.list()) {
String routeNamespace = Objects.requireNonNull(route.getMetadata()).getNamespace();
if (routeNamespace.equals(gatewayNamespace)) {
// Already handled in local routes search above
continue;
}
if (isBoundToGateway(route, gatewayNamespace, gatewayName)) {
Request req = new Request(route.getMetadata().getNamespace(), route.getMetadata().getName());
httpRouteWorkQueue.add(req);
LOG.info("Re-queued cross-namespace HTTPRoute {}/{} due to Gateway {}/{} reconciliation",
route.getMetadata().getNamespace(), route.getMetadata().getName(),
gatewayNamespace, gatewayName);
}
}
Comment on lines +120 to +140
if (hostnameConditions.isEmpty()) {
// No hostname: one selector+rule for this match
String selectorId = cache.generateSelectorId();
String selectorName = routeName + "-rule-" + ruleIndex;
SelectorData selectorData = buildSelectorData(selectorId, selectorName, matchConditions, upstreamList);
RuleData ruleData = buildRuleData(cache.generateRuleId(), selectorId, selectorName, matchConditions);
cache.addRouteSelector(namespace, routeName, PluginEnum.DIVIDE.getName(), selectorId);
routeConfigList.add(new IngressConfiguration(selectorData, List.of(ruleData), null));
} else {
// One selector+rule per hostname to keep AND semantics correct
for (ConditionData hostCondition : hostnameConditions) {
List<ConditionData> conditions = new ArrayList<>();
conditions.add(hostCondition);
conditions.addAll(matchConditions);

String selectorId = cache.generateSelectorId();
String selectorName = routeName + "-rule-" + ruleIndex;
SelectorData selectorData = buildSelectorData(selectorId, selectorName, conditions, upstreamList);
RuleData ruleData = buildRuleData(cache.generateRuleId(), selectorId, selectorName, conditions);
cache.addRouteSelector(namespace, routeName, PluginEnum.DIVIDE.getName(), selectorId);
routeConfigList.add(new IngressConfiguration(selectorData, List.of(ruleData), null));
Comment on lines +130 to +136
@Bean("gateway-controller-manager")
public ControllerManager gatewayControllerManager(
@Qualifier("gateway-shared-informer-factory") final SharedInformerFactory gatewayFactory,
@Qualifier("gateway-controller") final Controller gatewayController) {
ControllerManager controllerManager = new ControllerManager(gatewayFactory, gatewayController);
Executors.newSingleThreadExecutor().submit(controllerManager);
return controllerManager;
Comment on lines +139 to +145
@Bean("httproute-controller-manager")
public ControllerManager httpRouteControllerManager(
@Qualifier("httproute-shared-informer-factory") final SharedInformerFactory httpRouteFactory,
@Qualifier("httproute-controller") final Controller httpRouteController) {
ControllerManager controllerManager = new ControllerManager(httpRouteFactory, httpRouteController);
Executors.newSingleThreadExecutor().submit(controllerManager);
return controllerManager;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Support Kubernetes Gateway API

2 participants